package com.aldobucchi.ds.container.utils
{
	
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	import com.aldobucchi.ds.DataStructureEvent;
	import com.aldobucchi.ds.container.ContainerDiff;
	import com.aldobucchi.ds.container.IOContainer;
	import com.aldobucchi.x.flash.events.EventListenerSpec;
	
	
	/**
	 * 
	 * adds/removes event listeners as needed to all the elements within a Bag
	 * 
	 * @author aldo.bucchi@gmail.com
	 * @see com.aldobucchi.x.flash.events.EventListenerSpec
	 * 
	 */	
	public class ContainerElementsListenerHelper
	{
		
		
		
		////////////////////////////////////////////////////////////////////
		///////////////////////// container ////////////////////////////////
		////////////////////////////////////////////////////////////////////
		

		private var _container:IOContainer;
		
		
		public function get container( ):IOContainer
		{
			return _container;		
		}
		
		public function set container( v:IOContainer ):void
		{
			if ( _container == v )
				return;
			
			if ( _container != null )
				removeEventListeners( );
				
			_container = v;
			
			if ( _container != null )
				addEventListeners( );
		}
		

		
		////////////////////////////////////////////////////////////////////
		///////////////////////// listenerSpecs ////////////////////////////
		////////////////////////////////////////////////////////////////////

		
		private var listenerSpecs:Array;


		/**
		 * 
		 * We use this to avoid setting the same event listener twice
		 * 
		 */
		private var registry:Dictionary = new Dictionary( );

		
		
		////////////////////////////////////////////////////////////////////
		/////////////////////////// constructor ////////////////////////////
		////////////////////////////////////////////////////////////////////

	
		/**
		 * 
		 * 
		 * @param listenerSpecs an array of com.aldobucchi.x.flash.events.EventListenerSpec objects
		 * 
		 */	
		public function ContainerElementsListenerHelper( listenerSpecs:Array )
		{
			this.listenerSpecs = listenerSpecs;
		}
		
		
		
		
		
		private function addEventListeners( ):void
		{
			_container.addEventListener( DataStructureEvent.CHANGE, container_change );
			_container.walk( addListenerToElement );
		}

		
		
		private function removeEventListeners( ):void
		{
			_container.removeEventListener( DataStructureEvent.CHANGE, container_change );
			_container.walk( removeListenerFromElement );
		}
		
		
		
		
		private function addListenerToElement( e:*, multiplicity:int=1 ):void
		{
			
			if ( registry[ e ] )
				return;

			if ( ! e is IEventDispatcher )
				return;
				
			for each ( var spec:EventListenerSpec in listenerSpecs )
			{
				spec.addToDispatcher( e as IEventDispatcher );
			}
				
			registry[ e ] = true;			
		
		}
		
		
		
		
		private function removeListenerFromElement( e:*, multiplicity:int=1 ):void
		{

			if ( registry[ e ] )
			{
				for each ( var spec:EventListenerSpec in listenerSpecs )
				{
					spec.removeFromDispatcher( e as IEventDispatcher );
				}		
				delete registry[ e ];			
			}
		
		}
		
		
		
		
		
		private function container_change( event:DataStructureEvent ):void
		{

			var diff:ContainerDiff = event.diff as ContainerDiff;
			
			diff.addedElements.walk( addListenerToElement );
			
			diff.removedElements.walk( removeListenerFromElement );

		}
		
		
		
		
	}
	

}